/*
 * 智能语言控制控制，支持天猫、小爱、小度、google Assistent同时控制
 * 2022-08-22
 * QQ交流群：566565915
 * 官网https://bemfa.com
 */

#define server_ip "bemfa.com" // 巴法云服务器地址默认即可
#define server_port "8344"    // 服务器端口，tcp创客云端口8344

//********************需要修改的部分*******************//

#define wifi_name "Sensen" // WIFI名称，区分大小写，不要写错
// #define wifi_name "15367660966" // WIFI名称，区分大小写，不要写错

#define wifi_password "15367660" // WIFI密码

String UID = "4892bd7fe005ecbbccf35929157ec7e7"; // 用户私钥，可在控制台获取,修改为自己的UID
String aptype = "004";                           // 001插座类型，002灯类型，003风扇类型，004传感器，005空调，006开关，009窗帘
String TOPIC = "";                               // 主题名字，可在控制台新建
//**************************************************//
// 最大字节数
#define MAX_PACKETSIZE 512
// 设置心跳值60s
#define KEEPALIVEATIME 2 * 1000
// tcp客户端相关初始化，默认即可
int failCount = 0;          // 定义失败连接次数
String TcpClient_Buff = ""; // 初始化字符串，用于接收服务器发来的数据
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;    // 心跳
unsigned long preTCPStartTick = 0; // 连接
bool preTCPConnected = false;
bool taskStarted = false;

void startSTA();
void doWiFiTick();
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);
/**
 * 固件升级函数
 * 在需要升级的地方，加上这个函数即可，例如setup中加的updateBin();
 * 原理：通过http请求获取远程固件，实现升级
 */

// 当升级开始时，打印日志
void update_started()
{
  Serial.println("CALLBACK:  HTTP update process started");
}

// 当升级结束时，打印日志
void update_finished()
{
  Serial.println("CALLBACK:  HTTP update process finished");
}

// 当升级中，打印日志
void update_progress(int cur, int total)
{
  static bool update = false;
  int bfb = map(cur, 0, total, 0, 100);
  if (bfb % 5 == 0)
  {
    if (!update)
    {
      update = true;
      if (bfb == 100)
      {
        delay(1000);
      }
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "&msg=" + TOPIC + "#" + String(bfb) + " % \r\n");
      Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes... %d %\n", cur, total, bfb);
    }
  }
  else
  {
    update = false;
  }
  tftPrintf("" + String(bfb) + " %", TFT_RED);
}

// 当升级失败时，打印日志
void update_error(int err)
{
  Serial.printf("CALLBACK:  HTTP update fatal error code %d\n", err);
}

// 升级主程序函数
void updateBin()
{
  // tftPrintf("Update started");
  String upUrl = "http://bin.bemfa.com/b/3BcNDg5MmJkN2ZlMDA1ZWNiYmNjZjM1OTI5MTU3ZWM3ZTc=" + TOPIC + ".bin"; // OTA固件链接，请替换为自己的固件链接，如果接收到msg=update，开始执行固件升级
  WiFiClient UpdateClient;
  httpUpdate.onStart(update_started);     // 当升级开始时
  httpUpdate.onEnd(update_finished);      // 当升级结束时
  httpUpdate.onProgress(update_progress); // 当升级中
  httpUpdate.onError(update_error);       // 当升级失败时
  t_httpUpdate_return ret = httpUpdate.update(UpdateClient, upUrl);
  switch (ret)
  {
  case HTTP_UPDATE_FAILED: // 当升级失败
    Serial.println("当升级失败");
    vTaskDelete(NULL);
    break;
  case HTTP_UPDATE_NO_UPDATES: // 当无升级
    Serial.println("当无升级");
    break;
  case HTTP_UPDATE_OK: // 当升级成功
    Serial.println("当升级成功");
    break;
  }
}

/*
  网络连接
 */
void startSTA()
{
  IPAddress local_ip(192, 168, 0, 88);
  IPAddress gateway(192, 168, 0, 1);
  IPAddress subnet(255, 255, 255, 0);
  IPAddress dns1(8, 8, 8, 8);
  Serial.println("连接网络......");
  WiFi.mode(WIFI_STA);                          // STA模式
  WiFi.disconnect(true);                        // 断开连接
  WiFi.config(local_ip, gateway, subnet, dns1); // 设置静态IP，防止局域网内冲突
  WiFi.begin(wifi_name, wifi_password);         // 连接路由器
}

/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool startSTAFlag = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag)
  {
    startSTA();
    startSTAFlag = true;
    lastWiFiCheckTick = millis();
  }

  if (WiFi.status() != WL_CONNECTED) // 检查是否连接成功
  {
    if (millis() - lastWiFiCheckTick > 60000) // 未连接
    {
      startSTAFlag = false;
    }
    taskStarted = false;
  }
  else
  {
    if (taskStarted == false)
    {
      Serial.println("网络连接成功");
      Serial.print("IP地址:");
      Serial.println(WiFi.localIP());
      taskStarted = true;
    }
  }
}

//  微信消息推送函数
void doHttpStick(String msg)
{
  String type = "2"; // 1表示是预警消息，2表示设备提醒消息
  WiFiClient WiFi;
  HTTPClient http;
  String ApiUrl = "http://api.bemfa.com/api/wechat/v1/"; // 默认 api 网址
  String postData = "uid=" + UID + "&type=" + type + "&device=" + "并网控制器" + "&msg=" + msg;
  http.begin(WiFi, ApiUrl);                                            // Specify request destination
  http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Specify content-type header
  http.POST(postData);                                                 // Send the request
  http.end();                                                          // Close connection
}
/*
 *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{
  if (!TCPclient.print(p))
  {
    Serial.println("TCPclien发送失败");
    TCPclient.stop();
  }
  rtc_wdt_feed();          // 喂狗函数
  preHeartTick = millis(); // 心跳计时开始，需要每隔60秒发送一次数据
}
/*
 *初始化和服务器建立连接
 */
void startTCPClient()
{
  Serial.println("TCPclien开始连接");
  static bool client = false;
  if (TCPclient.connect(server_ip, atoi(server_port)))
  {
    String mac = WiFi.macAddress().substring(8);               // 取mac地址做主题用
    mac.replace(":", "");                                      // 去掉:号
    TOPIC = mac + aptype;                                      // 主题添加类型
    String tcpTemp = "";                                       // 初始化字符串
    tcpTemp = "cmd=1&uid=" + UID + "&topic=" + TOPIC + "\r\n"; // 构建订阅指令
    Serial.print("发送订阅指令:" + tcpTemp);
    sendtoTCPServer(tcpTemp); // 发送订阅指令
    tcpTemp = "";             // 清空
    /*
     //如果需要订阅多个主题，可发送  cmd=1&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=xxx1,xxx2,xxx3,xxx4\r\n
    教程：https://bbs.bemfa.com/64
     */
    TCPclient.setNoDelay(true);
    if (!client)
    {
      client = true;
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&/msg=" + rebootReason + "\r\n"); // 推送消息
    }
    Serial.println("TCPclien连接成功");
    grdOnOff = config.grdOnOff;
  }
  else
  {
    failCount = failCount + 1;
    if (failCount > 10) // 如果失败连接10次
    {
      failCount = 0;
      WiFi.disconnect(); // 断开连接
    }
    Serial.println();
    Serial.println("TCPclien连接失败");
    TCPclient.stop();
  }
  preTCPStartTick = millis();
}
/*
 *接收数据，发送数据
 */
void doTCPClientTick()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    preTCPStartTick = millis();
    return;
  }
  if (!TCPclient.connected()) // 断开重连
  {
    if (millis() - preTCPStartTick > 3000) // 重新连接
    {
      startTCPClient();
    }
  }
  else
  {
    while (TCPclient.available() && TcpClient_BuffIndex <= MAX_PACKETSIZE)
    {
      // 收数据
      char c = TCPclient.read();
      TcpClient_Buff += c;
      TcpClient_BuffIndex++;
    }
    if (millis() - preHeartTick >= KEEPALIVEATIME)
    {
      // 保持心跳

      // Serial.println("发送心跳");
      // sendtoTCPServer("cmd=0&msg=keep\r\n");
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC +
                      "/up&msg=" + (pwmDutyCycle > 0 ? "on" : "off") +
                      "#" + usagePower + "#" + gridPower +
                      "#" + usageCurrent + "#" + gridCurrent +
                      "#" + usagePowerFactor + "#" + gridPowerFactor +
                      "#" + usageVoltage +
                      "#" + pwmDutyCycle +
                      "#" + config.switchArray[0] +
                      "#" + config.switchArray[1] +
                      "#" + config.switchArray[2] +
                      "#" + config.switchArray[3] +
                      "#" + config.switchArray[4] +
                      "#" + config.switchArray[5] +
                      "#" + config.switchArray[6] +
                      "#" + config.switchArray[7] +
                      "#" + batteryVoltage +
                      "#" + batteryCurrent +
                      "#" + batteryPower +
                      "#" + batteryCharge +
                      "#" + batteryTemperature +
                      "#" + rebootReason + "\r\n"); // 推送消息
    }
    if (TcpClient_Buff.length() >= 1)
    {
      TCPclient.flush();
      if (TcpClient_Buff.indexOf("topic") >= 0)
      {
        Serial.println("接收数据:" + String(TcpClient_Buff.length()) + "字节" + TcpClient_Buff);
        int a = TcpClient_Buff.indexOf("msg=") + 4;
        int b = TcpClient_Buff.indexOf("=", a);
        int c = TcpClient_Buff.indexOf("\n", b);
        String d = TcpClient_Buff.substring(a, c);
        if (d.indexOf("update") >= 0)
        {
          Serial.println("升级");
          ledcWrite(0, 0);
          updateBin();
        }
        if (d.indexOf("pwm=") >= 0)
        {
          int pwmDutyCycle = atoi(TcpClient_Buff.substring(b + 1, c).c_str());
          ledcWrite(0, pwmDutyCycle);
          Serial.print("设置占空比: ");
          Serial.println(pwmDutyCycle);
          gridCycleTime = 10000;
        }
        else if (d.indexOf("K") >= 0)
        {
          Serial.println(d);
          preHeartTick = millis(); // 心跳计时开始
          changeSwitchStatusAndSendCommand(d);
          delay(100); // 延时100毫秒，等待数据稳定
        }
        else if (d.indexOf("grdOnOff") >= 0)
        {
          grdOnOff = !grdOnOff;
          config.grdOnOff = grdOnOff;
          ledcWrite(0, 0);
          saveConfig();
        }
      }
      TcpClient_Buff = "";
      TcpClient_BuffIndex = 0;
    }
  }
}

void sendtoData(String p)
{
  Serial.println(p);
  if (TCPclient.connected())
  {
    if (p.length() >= 125)
    {
      // 数据过长，先发送溢出提示
      TCPclient.print("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=数据溢出\r\n"); // 推送消息
      // 将P数据平均分成两份
      int mid = p.length() / 2;
      String part1 = p.substring(0, mid);
      TCPclient.print("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=" + part1 + "---\r\n"); // 推送前半部分消息
      String part2 = p.substring(mid);
      TCPclient.print("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=---" + part2 + "\r\n"); // 推送后半部分消息
    }
    else
    {
      TCPclient.print("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=" + p + "\r\n"); // 推送消息
    }
  }
}

/*
 *启动巴法云
 */
void loopBfy()
{
  doWiFiTick();
  doTCPClientTick();
}
